import csv
import os
import logging
from datetime import datetime
from typing import Dict, List, Any, Optional
from pymongo import MongoClient
from bson.objectid import ObjectId
from config import MONGODB_CONFIG, EXPORT_CONFIG, CSV_HEADERS
from progress_manager import ProgressManager


class HotelFacilityExporter:
    """酒店设施数据导出器"""
    
    def __init__(self):
        self.config = MONGODB_CONFIG
        self.export_config = EXPORT_CONFIG
        self.null_placeholder = EXPORT_CONFIG['null_placeholder']
        self.client = None
        self.db = None
        self.collection = None
        
        # 初始化日志
        self._setup_logging()
        
        # 初始化输出目录
        self._setup_output_dir()
        
        # CSV文件路径
        self.csv_paths = {
            'basic_facility': os.path.join(
                self.export_config['output_dir'],
                self.export_config['csv_files']['basic_facility']
            ),
            'facility_group': os.path.join(
                self.export_config['output_dir'],
                self.export_config['csv_files']['facility_group']
            )
        }
        
        # 数据缓冲区
        self.buffers = {
            'basic_facility': [],
            'facility_group': []
        }
        
        # 统计信息
        self.stats = {
            'total_hotels': 0,
            'processed_hotels': 0,
            'basic_facility_rows': 0,
            'facility_group_rows': 0,
            'error_count': 0
        }
        
        # 进度管理器
        self.progress_manager = ProgressManager(
            progress_dir=self.export_config.get('progress_dir', 'progress'),
            session_name='hotel_facility_export'
        )
    
    def _setup_logging(self):
        """设置日志"""
        log_dir = self.export_config['log_dir']
        os.makedirs(log_dir, exist_ok=True)
        
        timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
        log_file = os.path.join(log_dir, f'export_{timestamp}.log')
        
        # 文件日志 - 记录详细信息
        file_handler = logging.FileHandler(log_file, encoding='utf-8')
        file_handler.setLevel(logging.DEBUG)
        file_handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))
        
        # 控制台日志 - 只显示重要信息
        console_handler = logging.StreamHandler()
        console_handler.setLevel(logging.WARNING)  # 只显示警告和错误
        console_handler.setFormatter(logging.Formatter('%(levelname)s: %(message)s'))
        
        # 配置logger
        self.logger = logging.getLogger(__name__)
        self.logger.setLevel(logging.DEBUG)
        self.logger.addHandler(file_handler)
        self.logger.addHandler(console_handler)
    
    def _setup_output_dir(self):
        """创建输出目录"""
        output_dir = self.export_config['output_dir']
        os.makedirs(output_dir, exist_ok=True)
    
    def connect_mongodb(self):
        """连接MongoDB"""
        try:
            self.logger.info("正在连接MongoDB...")
            self.client = MongoClient(
                host=self.config['host'],
                port=self.config['port'],
                username=self.config['username'],
                password=self.config['password']
            )
            self.db = self.client[self.config['db_name']]
            self.collection = self.db[self.config['collection_name']]
            
            # 测试连接 - 使用 estimated_document_count() 更快
            try:
                self.stats['total_hotels'] = self.collection.estimated_document_count()
                self.logger.info(f"MongoDB连接成功！预估 {self.stats['total_hotels']} 条酒店数据")
            except:
                # 如果失败，设置为未知
                self.stats['total_hotels'] = 0
                self.logger.info(f"MongoDB连接成功！数据量未知")
            return True
        except Exception as e:
            self.logger.error(f"MongoDB连接失败: {str(e)}")
            return False
    
    def disconnect_mongodb(self):
        """断开MongoDB连接"""
        if self.client:
            self.client.close()
            self.logger.info("MongoDB连接已关闭")
    
    def _get_value(self, data: Any, default: str = None) -> str:
        """获取值，处理空值情况"""
        if default is None:
            default = self.null_placeholder
        
        if data is None:
            return default
        
        # 处理Int32类型
        if hasattr(data, '__class__') and 'Int32' in str(data.__class__):
            return str(data)
        
        # 处理空字符串
        if isinstance(data, str) and data == '':
            return ''
        
        # 处理布尔值
        if isinstance(data, bool):
            return str(data).lower()
        
        return str(data) if data != '' else default
    
    def _extract_basic_facility(self, hotel_data: Dict) -> List[List[str]]:
        """提取基本设施数据"""
        rows = []
        
        try:
            # 获取文档ID
            doc_id = str(hotel_data.get('_id', ''))
            
            # 获取酒店ID
            hotel_id = self._get_value(
                hotel_data.get('hotelBaseInfo', {}).get('masterHotelId')
            )
            
            if hotel_id == self.null_placeholder:
                self.logger.warning(f"酒店数据缺少masterHotelId，跳过: {hotel_data.get('_id')}")
                return rows
            
            # 获取facilityList（兼容顶层和嵌套路径；兼容列表或对象结构）
            facility_info = (
                hotel_data.get('hotelFacilityInfo')
                or hotel_data.get('hotelBaseInfo', {}).get('hotelFacilityInfo', {})
                or {}
            )
            raw_facility_list = facility_info.get('facilityList', [])
            if isinstance(raw_facility_list, dict):
                facility_list = raw_facility_list.get('facilityList', [])
            elif isinstance(raw_facility_list, list):
                facility_list = raw_facility_list
            else:
                facility_list = []
            
            # 调试日志
            self.logger.debug(f"酒店 {hotel_id}: facilityList 长度 = {len(facility_list)}")
            
            if not facility_list:
                return rows
            
            # 遍历设施列表
            for facility in facility_list:
                facility_type = self._get_value(facility.get('facilityTypeName'))
                facility_name = self._get_value(facility.get('facilityName'))
                
                # 处理chargeInfo
                charge_info = facility.get('chargeInfo', {})
                if charge_info:
                    chargeable = self._get_value(charge_info.get('chargeable'))
                    chargeable_desc = self._get_value(charge_info.get('chargeableDesc'))
                else:
                    chargeable = self.null_placeholder
                    chargeable_desc = self.null_placeholder
                
                rows.append([
                    doc_id,
                    hotel_id,
                    facility_type,
                    facility_name,
                    chargeable,
                    chargeable_desc
                ])
        
        except Exception as e:
            self.logger.error(f"提取基本设施数据失败: {str(e)}, 酒店ID: {hotel_data.get('_id')}")
            self.stats['error_count'] += 1
        
        return rows
    
    def _extract_facility_group(self, hotel_data: Dict) -> List[List[str]]:
        """提取设施集合数据"""
        rows = []
        
        try:
            # 获取文档ID
            doc_id = str(hotel_data.get('_id', ''))
            
            # 获取酒店ID
            hotel_id = self._get_value(
                hotel_data.get('hotelBaseInfo', {}).get('masterHotelId')
            )
            
            if hotel_id == self.null_placeholder:
                return rows
            
            # 获取facilityGroupList（兼容顶层和嵌套路径；兼容多种放置位置）
            facility_info = (
                hotel_data.get('hotelFacilityInfo')
                or hotel_data.get('hotelBaseInfo', {}).get('hotelFacilityInfo', {})
                or {}
            )
            facility_detail = facility_info.get('facilityDetail', {}) or {}
            facility_group_list = (
                facility_detail.get('facilityGroupList')
                or facility_info.get('facilityGroupList', [])
                or []
            )
            
            # 调试日志
            self.logger.debug(f"酒店 {hotel_id}: facilityGroupList 长度 = {len(facility_group_list)}")
            
            if not facility_group_list:
                return rows
            
            # 遍历设施集合列表
            for group in facility_group_list:
                group_type = self._get_value(group.get('facilityGroupTypeName'))
                group_id = self._get_value(group.get('facilityGroupId'))
                group_name = self._get_value(group.get('facilityGroupName'))
                
                # 获取设施项列表
                facility_item_list = group.get('facilityItemList', [])
                
                if not facility_item_list:
                    # 如果没有设施项，也记录一行（只有集合信息）
                    rows.append([
                        doc_id,
                        hotel_id,
                        group_type,
                        group_id,
                        group_name,
                        self.null_placeholder,  # 设施项名称
                        self.null_placeholder,  # 设施项编码
                        self.null_placeholder,  # 是否收费
                        self.null_placeholder,  # 收费描述
                        self.null_placeholder,  # 设施范围
                        self.null_placeholder   # 配备范围
                    ])
                else:
                    # 遍历设施项
                    for item in facility_item_list:
                        item_name = self._get_value(item.get('facilityItemName'))
                        item_id = self._get_value(item.get('facilityId'))
                        facility_limit = self._get_value(item.get('facilityLimit'))
                        equip_limit = self._get_value(item.get('equipLimit'))
                        
                        # 处理chargeInfo
                        charge_info = item.get('chargeInfo', {})
                        if charge_info:
                            chargeable = self._get_value(charge_info.get('chargeable'))
                            chargeable_desc = self._get_value(charge_info.get('chargeableDesc'))
                        else:
                            chargeable = self.null_placeholder
                            chargeable_desc = self.null_placeholder
                        
                        rows.append([
                            doc_id,
                            hotel_id,
                            group_type,
                            group_id,
                            group_name,
                            item_name,
                            item_id,
                            chargeable,
                            chargeable_desc,
                            facility_limit,
                            equip_limit
                        ])
        
        except Exception as e:
            self.logger.error(f"提取设施集合数据失败: {str(e)}, 酒店ID: {hotel_data.get('_id')}")
            self.stats['error_count'] += 1
        
        return rows
    
    def _write_buffer_to_csv(self, csv_type: str, force: bool = False):
        """将缓冲区数据写入CSV"""
        buffer = self.buffers[csv_type]
        
        # 添加详细日志
        self.logger.info(f"写入CSV前检查 - {csv_type}: 缓冲区大小 = {len(buffer)}, 强制写入 = {force}")
        
        if not buffer:
            self.logger.info(f"缓冲区为空，跳过写入 - {csv_type}")
            return
        
        if not force and len(buffer) < self.export_config['write_buffer_size']:
            self.logger.info(f"缓冲区大小未达到写入阈值，跳过写入 - {csv_type}")
            return
        
        try:
            csv_path = self.csv_paths[csv_type]
            file_exists = os.path.exists(csv_path)
            self.logger.info(f"准备写入CSV文件: {csv_path}, 文件已存在: {file_exists}")
            
            with open(csv_path, 'a', newline='', encoding='utf-8-sig') as f:
                writer = csv.writer(f)
                
                # 如果文件不存在，先写入表头
                if not file_exists:
                    self.logger.info(f"写入CSV表头 - {csv_type}")
                    writer.writerow(CSV_HEADERS[csv_type])
                
                # 写入数据
                writer.writerows(buffer)
                self.logger.info(f"成功写入 {len(buffer)} 行数据到 {csv_path}")
            
            # 更新统计
            if csv_type == 'basic_facility':
                self.stats['basic_facility_rows'] += len(buffer)
            else:
                self.stats['facility_group_rows'] += len(buffer)
            
            # 清空缓冲区
            buffer.clear()
            self.logger.info(f"缓冲区已清空 - {csv_type}")
            
        except Exception as e:
            self.logger.error(f"写入CSV失败 ({csv_type}): {str(e)}")
            self.stats['error_count'] += 1
    
    def export(self):
        """执行导出"""
        try:
            # 连接数据库
            if not self.connect_mongodb():
                return False
            
            # 检查是否启用断点续传
            enable_resume = self.export_config.get('enable_resume', True)
            last_processed_id = None
            
            if enable_resume:
                # 尝试加载进度
                if self.progress_manager.load_progress():
                    last_processed_id = self.progress_manager.get_last_processed_id()
                    # 恢复统计信息
                    self.stats['processed_hotels'] = self.progress_manager.get_processed_count()
                    self.stats['basic_facility_rows'] = self.progress_manager.progress_data.get('basic_facility_rows', 0)
                    self.stats['facility_group_rows'] = self.progress_manager.progress_data.get('facility_group_rows', 0)
                    self.stats['error_count'] = self.progress_manager.progress_data.get('error_count', 0)
                    
                    self.logger.info(f"✓ 将从酒店ID {last_processed_id} 之后继续处理")
                    print(f"\n✓ 检测到上次进度，将继续处理...")
                    print(f"  已处理: {self.stats['processed_hotels']} 条")
                    print(f"  基本设施: {self.stats['basic_facility_rows']} 行")
                    print(f"  设施集合: {self.stats['facility_group_rows']} 行")
                    print(f"  错误数: {self.stats['error_count']} 条\n")
            else:
                # 清除旧进度，重新开始
                self.progress_manager.clear_progress()
                self.logger.info("断点续传已禁用，将从头开始处理")
            
            print("\n" + "="*70)
            print("开始导出数据...")
            print(f"测试模式: {'是' if self.export_config.get('test_limit') else '否'}")
            print(f"断点续传: {'是' if enable_resume else '否'}")
            print(f"总酒店数: {self.stats['total_hotels']:,}")
            print("="*70)
            
            self.logger.info("开始导出数据...")
            start_time = datetime.now()
            
            # 批量读取并处理
            batch_size = self.export_config['batch_size']
            test_limit = self.export_config.get('test_limit')
            
            # 构建查询条件
            query = {}
            if last_processed_id:
                # 从上次中断的地方继续（使用 _id 大于上次处理的ID）
                try:
                    query = {'_id': {'$gt': ObjectId(last_processed_id)}}
                except:
                    self.logger.warning(f"无法解析上次的ID: {last_processed_id}，将从头开始")
                    query = {}
            
            # 按 _id 排序查询（保证顺序导出）
            if test_limit:
                cursor = self.collection.find(query).sort('_id', 1).limit(test_limit).batch_size(batch_size)
                self.logger.info(f"⚠️  测试模式：仅处理前 {test_limit} 条数据")
            else:
                cursor = self.collection.find(query).sort('_id', 1).batch_size(batch_size)
            
            self.logger.info("✓ 按 _id 顺序导出数据")
            
            progress_save_interval = self.export_config.get('progress_save_interval', 100)
            
            for hotel_data in cursor:
                current_doc_id = str(hotel_data.get('_id'))
                hotel_id = hotel_data.get('hotelBaseInfo', {}).get('masterHotelId', '未知')
                
                try:
                    # 提取基本设施
                    basic_rows = self._extract_basic_facility(hotel_data)
                    if basic_rows:
                        self.buffers['basic_facility'].extend(basic_rows)
                        self._write_buffer_to_csv('basic_facility')
                    
                    # 提取设施集合
                    group_rows = self._extract_facility_group(hotel_data)
                    if group_rows:
                        self.buffers['facility_group'].extend(group_rows)
                        self._write_buffer_to_csv('facility_group')
                    
                    # 更新进度
                    self.stats['processed_hotels'] += 1
                    
                    # 定期保存进度
                    if self.stats['processed_hotels'] % progress_save_interval == 0:
                        self.progress_manager.save_progress(
                            last_processed_id=current_doc_id,
                            processed_count=self.stats['processed_hotels'],
                            basic_facility_rows=self.stats['basic_facility_rows'],
                            facility_group_rows=self.stats['facility_group_rows'],
                            error_count=self.stats['error_count']
                        )
                    
                    # 测试模式下每条都输出进度，正常模式每1000条输出一次
                    progress_interval = 1 if test_limit else 1000
                    if self.stats['processed_hotels'] % progress_interval == 0:
                        if self.stats['total_hotels'] > 0:
                            progress = (self.stats['processed_hotels'] / self.stats['total_hotels']) * 100
                            progress_str = f"{progress:.2f}%"
                        else:
                            progress_str = "N/A"
                        
                        # 控制台显示简洁进度
                        print(f"\r进度: {self.stats['processed_hotels']:,}/{self.stats['total_hotels']:,} ({progress_str}) | "
                              f"基本设施: {self.stats['basic_facility_rows']:,}行 | "
                              f"设施集合: {self.stats['facility_group_rows']:,}行 | "
                              f"错误: {self.stats['error_count']}条", end='', flush=True)
                        
                        # 文件日志记录详细信息
                        self.logger.info(
                            f"进度: {self.stats['processed_hotels']} {progress_str} - "
                            f"基本设施: {self.stats['basic_facility_rows']}行, "
                            f"设施集合: {self.stats['facility_group_rows']}行, "
                            f"错误: {self.stats['error_count']}条"
                        )
                
                except Exception as e:
                    error_msg = str(e)
                    self.logger.error(f"处理酒店数据失败: {error_msg}, 酒店ID: {hotel_id}, 文档ID: {current_doc_id}")
                    self.stats['error_count'] += 1
                    
                    # 记录错误到文件
                    self.progress_manager.log_error(
                        hotel_id=hotel_id,
                        error_type='processing_error',
                        error_message=error_msg
                    )
                    
                    # 即使出错也要更新进度计数
                    self.stats['processed_hotels'] += 1
                    continue
            
            # 换行，结束进度条
            print("\n")
            
            # 写入剩余缓冲区数据
            self._write_buffer_to_csv('basic_facility', force=True)
            self._write_buffer_to_csv('facility_group', force=True)
            
            # 保存最终进度（标记为完成）
            if enable_resume and cursor:
                self.progress_manager.save_progress(
                    last_processed_id=current_doc_id if 'current_doc_id' in locals() else 'completed',
                    processed_count=self.stats['processed_hotels'],
                    basic_facility_rows=self.stats['basic_facility_rows'],
                    facility_group_rows=self.stats['facility_group_rows'],
                    error_count=self.stats['error_count']
                )
                self.logger.info("✓ 最终进度已保存")
            
            # 计算耗时
            end_time = datetime.now()
            duration = (end_time - start_time).total_seconds()
            hours = int(duration // 3600)
            minutes = int((duration % 3600) // 60)
            seconds = int(duration % 60)
            
            # 控制台输出简洁统计
            print("="*70)
            print("✓ 导出完成！")
            print(f"总耗时: {hours}小时{minutes}分{seconds}秒" if hours > 0 else f"{minutes}分{seconds}秒" if minutes > 0 else f"{seconds}秒")
            print(f"处理酒店数: {self.stats['processed_hotels']:,}/{self.stats['total_hotels']:,}")
            print(f"基本设施行数: {self.stats['basic_facility_rows']:,}")
            print(f"设施集合行数: {self.stats['facility_group_rows']:,}")
            print(f"错误数: {self.stats['error_count']}")
            print(f"输出目录: {os.path.abspath(self.export_config['output_dir'])}")
            print("="*70)
            
            # 文件日志记录详细信息
            self.logger.info("=" * 60)
            self.logger.info("导出完成！")
            self.logger.info(f"总耗时: {duration:.2f}秒")
            self.logger.info(f"处理酒店数: {self.stats['processed_hotels']}/{self.stats['total_hotels']}")
            self.logger.info(f"基本设施数据行数: {self.stats['basic_facility_rows']}")
            self.logger.info(f"设施集合数据行数: {self.stats['facility_group_rows']}")
            self.logger.info(f"错误数: {self.stats['error_count']}")
            self.logger.info(f"输出文件:")
            self.logger.info(f"  - {self.csv_paths['basic_facility']}")
            self.logger.info(f"  - {self.csv_paths['facility_group']}")
            self.logger.info("=" * 60)
            
            return True
            
        except Exception as e:
            self.logger.error(f"导出过程发生错误: {str(e)}")
            return False
        
        finally:
            self.disconnect_mongodb()



